package com.fowo.api.sys.component;

import com.fowo.api.sys.service.SysUserService;
import com.fowo.api.user.model.JwtUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;

/**
 * 提供访问权限管理
 */
@Slf4j
@Component("am")
public class AccessManager {
    private static final String REDIS_AUTHORITY_KEY_FORMAT = "USER_AUTHORITY_%d";
    @Resource
    private SysUserService sysUserService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 包括 1 分钟缓存版本的 sysUserService.getAllAuthorityByUserId
    private List<String> getAllAuthorityByUserId(Long userId) {
        String key = String.format(REDIS_AUTHORITY_KEY_FORMAT, userId);
        List<String> authority = (List<String>) redisTemplate.opsForValue().get(key);
        if (authority != null) {
            return authority;
        }
        authority = sysUserService.getAllAuthorityByUserId(userId, null);
        if (authority != null) {
            redisTemplate.opsForValue().set(key, authority, Duration.ofMinutes(1));
        }
        return authority;
    }

    /**
     * 测试是否有指定访问权限
     * @param allows 允许访问选项
     * @param authority 权限名称，多个使用半角逗号分隔
     * @return 当前登录者是否有仅限
     */
    public boolean canAccess(AccessAllows allows, String authority) {
        JwtUserInfo jwtUserInfo = JwtUserInfo.fromHeader();
        if (jwtUserInfo != null && JwtUserInfo.TYPE_GUEST.equals(jwtUserInfo.getType())) {
            // 来宾访问
            return allows == AccessAllows.Guest;
        }
        if (jwtUserInfo != null && jwtUserInfo.getUserId() != null && jwtUserInfo.getUserId() > -1L) {
            if(StringUtils.hasText(authority)) {
                List<String> needAuthority = Arrays.asList(authority.split("\\s*,\\s*").clone());
                List<String> allAuthority = getAllAuthorityByUserId(jwtUserInfo.getUserId());
                log.debug("权限测试 - allAuthority:{} needAuthority:{}", allAuthority, needAuthority);
                if (allows == AccessAllows.AllAuthority) {
                    return allAuthority.containsAll(needAuthority);
                }
                return needAuthority.stream().anyMatch(allAuthority::contains);
            }
            return true;
        }
        return false;
    }

    /**
     * 测试是否有指定访问权限
     * @param authority 权限名称，多个使用半角逗号分隔
     * @return 当前登录者是否有仅限
     */
    public boolean canAccess(String authority) {
        return canAccess(AccessAllows.AnyAuthority, authority);
    }

    /**
     * 允许访问类型
     */
    public enum AccessAllows {
        /** 满足 authority 中的任何一个权限 */
        AnyAuthority,
        /** 满足 authority 中的所有权限 */
        AllAuthority,
        /** 来宾可访问 */
        Guest,
    }
}
